package ar.edu.itba.pod.legajo46470.cluster.ui;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;

import ar.edu.itba.node.NodeInformation;

public class ClusterAdministrationUI {
	private static ClusterAdministrationUI instance = null;
	private NodeInformation clusterNodeInfo;
	private InetAddress remoteHost;
	private int remotePort;
	private BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	
    // Private constructor suppresses 
    private ClusterAdministrationUI() {}
 
    // creador sincronizado para protegerse de posibles problemas  multi-hilo
    // otra prueba para evitar instanciación múltiple 
    private synchronized static void createInstance() {
        if (instance == null) { 
            instance = new ClusterAdministrationUI();
        }
    }
 
    public static ClusterAdministrationUI getInstance() {
        if (instance == null) createInstance();
        return instance;
    }
    
    public void configureCluster() throws IOException {
    	NodeInformation nodeInfo;
    	String createAns, id, host, lport, rport;
    	boolean hosterr, localporterr, remoteporterr, firstTime, local;
    	
    	hosterr = localporterr = remoteporterr = local = false;
    	firstTime = true;
    	host = lport = rport = id = "";
    	nodeInfo = null;
    	remoteHost = null;
    	remotePort = 0;

        //  open up standard input
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	
    	System.out.println("Create New Cluster? [y/n]");
    	createAns = br.readLine();
    	
    	if (createAns.toUpperCase().trim().equals("Y")) {
    		local = true;
    	}
    	
    	do {
    		
			// always asks for local port to create rmi registry on that port
			if (firstTime || localporterr) {
				System.out.println("Enter local port (e.g. 1099)");
				lport = br.readLine();
			}
    		
			try {
				id = getFirstNonLoopbackAddress(true,false).getHostAddress().toString() + ":" + lport;
				//id = "9";
    			// Creates node information if local port is correct
        		nodeInfo = new NodeInformation(getFirstNonLoopbackAddress(true,false).getHostAddress(),
        				Integer.parseInt(lport), id);
			} catch (NumberFormatException e) {
				localporterr = true;
				System.err.println("Incorrect local port number");
			}
        		
    		try {
				// asks for host if user selects connecting to a group
	    		if ((firstTime || hosterr) && !local) {
	    			System.out.println("Enter host (e.g. 10.2.0.36)");
	    			host = br.readLine();
	    			// prevents incorrect IP's
	    			remoteHost = InetAddress.getByName(host);
	    		}
    		} catch (UnknownHostException e) {
        		System.err.println(e.getMessage());
        		hosterr = true;
    		}
	    		
    		// asks for remote port only if connecting to a group
    		try {
	    		if ((firstTime || remoteporterr) && !local) {
		        	System.out.println("Enter remote port (e.g. 1099)");
		        	rport = br.readLine();
		        	
		        		// prevents incorrect remote port
		        		remotePort = Integer.parseInt(rport);
    		}
    		} catch (NumberFormatException e) {
    			remoteporterr = true;
    			System.err.println("Incorrect remote port number");
    		}
    		
    	} while(hosterr || localporterr || remoteporterr);
        
        this.clusterNodeInfo = nodeInfo;
    }
    
    public NodeInformation getClusterNode() {
    	return this.clusterNodeInfo;
    }

    public MenuOption menu() throws IOException {
    	MenuOption moption = null;
    	boolean error;
    	int option;
    	
    	System.out.println("Select option: ");
    	System.out.println("1 - Start simulation");
    	System.out.println("2 - Get Group ID");
    	System.out.println("3 - Show Agents Number");
    	System.out.println("4 - Show Neighbours");
    	System.out.println("5 - Add agents");
    	System.out.println("6 - Node Statistics");
    	System.out.println("7 - Disconnect from Group");
    	System.out.println("8 - Disconnect and Exit");

    	do {
    		try {
    			error = false;
    			option = Integer.parseInt(br.readLine().trim());
    			
    			if (option < 1 || option > 8) {
    				System.err.println("Invalid option");
    				error = true;
    			} else {
    				moption = MenuOption.values()[option-1];
    			}
    		} catch (NumberFormatException e) {
    			error = true;
    		}
    	} while(error);
    	
    	return moption;
    }
    
	public InetAddress getRemoteHost() {
		return remoteHost;
	}

	public int getRemotePort() {
		return remotePort;
	}
	
	public boolean createOptionSelected() {
		return remoteHost == null;
	}
	
	public UIAgentChooser agentMenu() {
		int amount;
		String type = "2";
		String restype = "1";
		ResourceOption roption;
		AgentTypeOption aoption;
		UIAgentChooser achooser;
		
		
		try {
			try {
				System.out.println("Agent type? 1-Market, 2-Producer, 3-Consumer, default: Producer");
				type = br.readLine();
				aoption = AgentTypeOption.values()[Integer.parseInt(type)-1];
			} catch (NumberFormatException e) {
				System.err.println("Invalid Agent type, defaulting to producer");
				aoption = AgentTypeOption.PRODUCER;
			} catch (ArrayIndexOutOfBoundsException e) {
				System.err.println("Invalid Agent type, defaulting to producer");
				aoption = AgentTypeOption.PRODUCER;
			}
			
			try {
				System.out.println("Resource type? 1-Steel, 2-Copper, 3-Gold, default: Steel");
				restype = br.readLine();
				roption = ResourceOption.values()[Integer.parseInt(restype)-1];
			} catch (NumberFormatException e) {
				System.err.println("Invalid Resource type, defaulting to Steel");
				roption = ResourceOption.STEEL;
			} catch (ArrayIndexOutOfBoundsException e) {
				System.err.println("Invalid Resource type, defaulting to Steel");
				roption = ResourceOption.STEEL;
			} 
			
			try {
				System.out.println("Amount to create? (MAX = 20)");
				amount = Integer.parseInt(br.readLine());
				
				if (amount < 1 || amount > 20) {
					System.err.println("Amount must be between 1 and 20, defaulting to 1");
					amount = 1;
				}
			} catch (NumberFormatException e) {
				System.err.println("Invalid amount, defaulting to 1");
				amount = 1;
			}
						
			achooser = new UIAgentChooser(roption, aoption, amount);
			
		} catch (IOException e) {
			System.err.println("Error reading user input, defaulting to 1 Producer-Steel");
			achooser = new UIAgentChooser(ResourceOption.STEEL, AgentTypeOption.PRODUCER, 1);
		}
		
		return achooser;
	}
	
	private static InetAddress getFirstNonLoopbackAddress(boolean preferIpv4, boolean preferIPv6) throws SocketException {
	    Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
	    while (en.hasMoreElements()) {
	        NetworkInterface i = (NetworkInterface) en.nextElement();
	        for (Enumeration<InetAddress> en2 = i.getInetAddresses(); en2.hasMoreElements();) {
	            InetAddress addr = (InetAddress) en2.nextElement();
	            if (!addr.isLoopbackAddress()) {
	                if (addr instanceof Inet4Address) {
	                    if (preferIPv6) {
	                        continue;
	                    }
	                    return addr;
	                }
	                if (addr instanceof Inet6Address) {
	                    if (preferIpv4) {
	                        continue;
	                    }
	                    return addr;
	                }
	            }
	        }
	    }
	    return null;
	}
	
	public void disposeMenu() {
		try {
			br.close();
		} catch (IOException e) {
			System.err.println("Error closing stream");
		}
	}
}
